package cn.gs.meta.ops;

// @author gs
import cn.gs.meta.db.tables.Keep;
import cn.gs.meta.db.tables.ModelBase;
import cn.gs.meta.db.tables.Ogn;
import cn.gs.util.Eval;
import cn.gs.util.Pair;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;

public class OgnKeepOps {

    Ogn ogn;

    void putUsed(Keep keep, ModelBase model, double count) {
        Double d = keep.getUsedAlter().get(model);
        if (d == null) {
            d = 0.0;
        }
        if (Eval.isZero(d + count)) {
            keep.getUsedAlter().remove(model);
        } else {
            keep.getUsedAlter().put(model, d + count);
        }
    }

    void resetKeep(Keep k) {
        k.setAlteration(null);
        k.setCnt(0);
        k.setC1(0);
        k.setC2(0);
        k.setC3(0);
        k.setC4(0);
        k.setC5(0);
        k.setUsed(0);
        k.setU1(0);
        k.setU2(0);
        k.setU3(0);
        k.setU4(0);
        k.setU5(0);
        k.getUsedAlter().clear();
    }
    private int level;
    private Map<ModelBase, Keep> now = new HashMap<>();
    private Map<ModelBase, Stack<Pair<Integer, Keep>>> stack = new HashMap<>();

    public Keep peek(ModelBase model) {
        if (level <= 0) {
            Keep k = ogn.getKeeps().get(model);
            if (k == null) {
                k = new Keep();
                ogn.getKeeps().put(model, k);
            }
            return k;
        } else {
            Keep keep = now.get(model);
            if (keep == null) {
                Stack<Pair<Integer, Keep>> s = stack.get(model);
                if (s == null) {
                    s = new Stack<>();
                    stack.put(model, s);
                }
                while (!s.empty() && s.lastElement().getFirst() >= level) {
                    s.pop();
                }
                keep = new Keep();
                if (s.empty()) {
                    keep.copyFrom(ogn.getKeeps().get(model));
                } else {
                    keep.copyFrom(s.lastElement().getSecond());
                }
                now.put(model, keep);
            }
            return keep;
        }
    }

    public void begin() {
        for (Entry<ModelBase, Keep> e : now.entrySet()) {
            if (!stack.containsKey(e.getKey())) {
                stack.put(e.getKey(), new Stack<Pair<Integer, Keep>>());
            }
            stack.get(e.getKey()).push(new Pair<>(level, e.getValue()));
        }
        now.clear();
        level++;
    }

    public void commit() {
        level--;
        if (level <= 0) {
            for (Entry<ModelBase, Keep> e : now.entrySet()) {
                Keep keep = ogn.getKeeps().get(e.getKey());
                if (keep == null) {
                    keep = new Keep();
                    ogn.getKeeps().put(e.getKey(), keep);
                }
                keep.copyFrom(e.getValue());
            }
            stack.clear();
            now.clear();
        } else {
            Map<ModelBase, Keep> newnow = new HashMap<>();
            for (Entry<ModelBase, Keep> e : now.entrySet()) {
                Stack<Pair<Integer, Keep>> s = stack.get(e.getKey());
                if (s == null) {
                    s = new Stack<>();
                    stack.put(e.getKey(), s);
                }
                while (!s.empty() && s.lastElement().getFirst() > level) {
                    s.pop();
                }
                if (!s.empty() && s.lastElement().getFirst() == level) {
                    Pair<Integer, Keep> p = s.pop();
                    p.getSecond().copyFrom(e.getValue());
                    newnow.put(e.getKey(), p.getSecond());
                } else {
                    newnow.put(e.getKey(), e.getValue());
                }
            }
            now = newnow;
        }
    }

    public void rollback() {
        if (level > 0) {
            level--;
            now.clear();
            for (Entry<ModelBase, Stack<Pair<Integer, Keep>>> e : stack.entrySet()) {
                Stack<Pair<Integer, Keep>> s = e.getValue();
                if (!s.empty()) {
                    while (!s.empty() && s.lastElement().getFirst() > level) {
                        s.pop();
                    }
                    if (!s.empty() && s.lastElement().getFirst() == level) {
                        now.put(e.getKey(), s.pop().getSecond());
                    }
                }
            }
        }
    }

    public Ogn getOgn() {
        return ogn;
    }

    public void setOgn(Ogn ogn) {
        this.ogn = ogn;
    }
}
